use std::{fs, fs::File, path::Path};

use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use smart_default::SmartDefault;

#[derive(Debug, Deserialize, SmartDefault,Serialize)]
pub struct Configs {
    #[serde(default)]
    pub server: Server,
    #[serde(default)]
    pub log: Log,
    #[serde(default)]
    pub cert: Cert,
    #[serde(default)]
    pub jwt: Jwt,
}

#[derive(Debug, Deserialize, SmartDefault,Serialize)]
pub struct Server {
    #[serde(default)]
    #[default("npm-proxy")]
    pub name: String,
    #[serde(default)]
    #[default("0.0.0.0:5800")]
    pub address: String,
    #[serde(default)]
    #[default(vec ! [])]
    pub cors_allow_origin: Vec<String>,
    #[serde(default)]
    pub ssl: bool,
    #[serde(default)]
    #[default("https://mirrors.huaweicloud.com/repository/npm")]
    pub registry: String,
    #[serde(default)]
    #[default(false)]
    pub offline: bool,
    #[serde(default)]
    #[default(30)]
    pub cache_time: usize
}

#[derive(Debug, Deserialize, SmartDefault,Serialize)]
pub struct Log {
    #[serde(default)]
    #[default("info")]
    pub filter_level: String,
    #[serde(default)]
    #[default(true)]
    pub with_ansi: bool,
    #[serde(default)]
    #[default(true)]
    pub to_stdout: bool,
    #[serde(default)]
    #[default("./logs".to_string())]
    pub directory: String,
    #[serde(default)]
    #[default("server.log")]
    pub file_name: String,
    #[serde(default)]
    #[default("daily")]
    pub rolling: String,
}

#[derive(Debug, Deserialize, SmartDefault,Serialize)]
pub struct Jwt {
    #[serde(default)]
    #[default("secret")]
    pub jwt_secret: String,
    #[serde(default)]
    #[default(6000)]
    pub jwt_exp: i64,
}

#[derive(Debug, Deserialize, SmartDefault,Serialize)]
pub struct Cert {
    /// cert
    #[serde(default)]
    #[default("config/certs/cert.pem")]
    pub cert: String,
    /// key
    #[serde(default)]
    #[default("config/certs/key.pem")]
    pub key: String,
}

const CONFIG_FILE: &str = "config/config.yml";

pub static CFG: Lazy<Configs> = Lazy::new(self::Configs::init);

impl Configs {
    pub fn init() -> Self {
//        let mut file = match File::open(CONFIG_FILE) {
//            Ok(f) => f,
//            Err(e) => panic!(
//                "Configuration file does not exist:{},error message:{}",
//                CONFIG_FILE, e
//            ),
//        };
//        let mut cfg_contents = String::new();
//        match file.read_to_string(&mut cfg_contents) {
//            Ok(s) => s,
//            Err(e) => panic!("Failed to read configuration file, error message:{}", e),
//        };
//        match serde_yaml::from_str(&cfg_contents) {
//            Ok(c) => c,
//            Err(e) => panic!("Failed to parse configuration file, error message:{}", e),
//        }
        File::open(CONFIG_FILE)
            .map(|f| {
                serde_yaml::from_reader(f).unwrap_or_default()
            })
            .or_else(|e| {
                let path = Path::new(CONFIG_FILE);
                fs::create_dir_all(path.parent().unwrap()).ok();
                fs::write(path, serde_yaml::to_string(&Self::default()).unwrap()).ok();
                Err(e.to_string())
            }).unwrap_or_default()
    }
}

pub static CERT_KEY: Lazy<CertKey> = Lazy::new(get_cert_key);

pub struct CertKey {
    pub cert: Vec<u8>,
    pub key: Vec<u8>,
}

impl CertKey {
    pub fn new(cert: Vec<u8>, key: Vec<u8>) -> Self {
        Self { cert, key }
    }
}

fn get_cert_key() -> CertKey {
    let cert = get_string(&CFG.cert.cert);
    let key = get_string(&CFG.cert.key);
    CertKey::new(cert, key)
}

fn get_string<P: AsRef<Path>>(path: P) -> Vec<u8> {
    std::fs::read(path).expect("failed to read file")
}
